﻿(function () {

	function useAttr(key, value) {
		if (isUnde(window[key])) {
			window[key] = value;
		}
	}

	function useAttrObjects(map) {
		for (const [key, value] of Object.entries(map)) {
			useAttr(key, value)
		}
	}


	var c_url = window.location.href;

	// 常量定义
	const yunConfig = [
		{ "host": "ai.abc5g.com", "ilogo": "/images/ilogo.png", "blogo": "/images/blogo.png", "vlogo": "https://api.abc5g.com/images/blogo.png" },
		{ "host": "api.abc5g.com", "ilogo": "/images/nkilogo.png", "blogo": "/images/nkblogo.png", "vlogo": "https://api.abc5g.com/images/blogo.png" },
		{ "host": "localhost", "ilogo": "/images/nkilogo.png", "blogo": "/images/nkblogo.png", "vlogo": "https://api.abc5g.com/images/blogo.png" },
	]
	// 列表加载状态
	const ListLoadStatus = {
		Loadding: 1,	// 正在加载
		NoData: 2,		// 无数据
		Success: 3,		// 有数据
	}

	var yun_hostname = window.location.hostname;
	var usetheme = {};
	for (var i = 0; i < yunConfig.length; i++) {
		if (yunConfig[i].host == yun_hostname) {
			usetheme = yunConfig[i];
			break;
		}
	}

	// 获取url参数
	function GetQueryString(name) {
		var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
		var r = window.location.search.substr(1).match(reg); //获取url中"?"符后的字符串并正则匹配
		var context = "";
		if (r != null)
			context = decodeURIComponent(r[2]);
		reg = null;
		r = null;
		return context == null || context == "" || context == "undefined" ? "" : context;
	}

	
	var apiurl = '/apifilter/ApiRoute.ashx';
	var apierrorTime = 1;

	var OT = new function () {
		this.Proc = "/DataManager/Proc";
		this.ProcAddSet = "/DataManager/ProcAddSet";
		this.ProcSet = "/DataManager/ProcSet";
	}

	//var RES_OT = new function () {
	//	this.Proc = "https://api.abc5g.com/apis/DataManager/Proc";
	//	this.ProcAddSet = "https://api.abc5g.com/apis/DataManager/ProcAddSet";
	//	this.ProcSet = "https://api.abc5g.com/apis/DataManager/ProcSet";
	//}

	//var INFO_OT = new function () {
	//	this.Proc = "https://info.abc5g.com/apis/DataManager/Proc";
	//	this.ProcAddSet = "https://info.abc5g.com/apis/DataManager/ProcAddSet";
	//	this.ProcSet = "https://info.abc5g.com/apis/DataManager/ProcSet";
	//}

	// 接口请求等待画面（遮罩层）
	function loaddingPlus(slt) {
		// 使用 ElementPlus 的加载等待效果
		if (_.has(window, "ElementPlus.ElLoading.service")) {
			if (!!slt) {
				window.globalLoadding = ElementPlus.ElLoading.service({
					lock: true,
					text: '正在加载',
					background: 'rgba(0, 0, 0, 0.7)',
				})
			} else {
				window.globalLoadding.close()
			}
			return;
		}

		// 使用 ElementUI 的加载等待效果
		if (_.has(window, "ELEMENT.Loading")) {
			if (!!slt) {
				window.globalLoadding = ELEMENT.Loading.service({
					lock: true,
					text: '正在加载',
					background: 'rgba(0, 0, 0, 0.7)',
				})
			} else {
				window.globalLoadding.close()
			}
			return;
		}

		// 使用 ElementUI 的加载等待效果
		if (_.has(window, "vueApp.$loading")) {
			if (!!slt) {
				window.globalLoadding = vueApp.$loading({
					lock: true,
					text: '正在加载',
					spinner: 'el-icon-loading',
					background: 'rgba(0, 0, 0, 0.7)'
				});
			} else {
				console.log('window.globalLoadding', window.globalLoadding);
				window.globalLoadding.close()
			}
		}

		// 使用原生方式加载等待效果
		try {
			const $mask = document.getElementById('ktlapimask');
			if ($mask == null) {
				var container = document.createElement("div")
				container.innerHTML = '<div id="ktlapimask" style="display:none; position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 9999; background: rgba(0,0,0,0.5);">'
					+ '<div style=" margin: 66px auto; width: calc(50% - 250px);" >'
					+ '<img src="/images/mxb.gif" />'
					+ '</div>'
					+ '</div> ';
				document.body.appendChild(container);
			}
			try {
				$mask.style.display = slt ? 'block' : 'none';
			} catch (e) {

			}
		} catch (ex) {
			console.log("loadding mask 异常")
			console.log(ex);
		}
	}

	function ktlapi(opts, others = {}, env = 'default') {
		var per = "/";
		let _defaule_ot = OT;
		switch (env) {
			case "res":
				per = "https://api.abc5g.com/apis/";
				break;
			case "info":
				per = "https://info.abc5g.com/apis/";
				break;
			default:
				break;
		}

		// 参数校验
		if (isEmptyObj(opts)) {
			MsgBox.error("ktlapi参数有误")
			return new Promise((res, rej) => { })
		}

		// islock：是否队列请求，默认为true
		const { islock = true } = others

		// 请求队列状态记录，防止重复请求
		var paramsStr = JSON.stringify(opts);
		if(islock){
			if (ktlapi.requestMap != [] && ktlapi.requestMap[paramsStr]) { // 重复请求，返回
				return new Promise((res, rej) => { })
			}
		}
		ktlapi.requestMap[paramsStr] = true;

		return new Promise((resolve, reject) => {
			// 默认参数
			let defaults = {
				method: 'post',
				url: "", // 非DataManager请求地址
				apiurl: "", // DataManager请求地址
				data: "", // 请求参数
				dataType: "",
				async: true,
				cache: true,
			};

			// 配置项
			let ktlsetting = {
				"islist": false,
				"opuid": 0,
				"mask": true,
			}

			// 参数同步
			for (var key in opts) {
				defaults[key] = opts[key];
			}

			// 配置项同步
			if (!isEmpty(others)) {
				if (isBoolean(others)) {
					ktlsetting.islist = others;
				} else {
					ktlsetting = {
						...ktlsetting,
						...others
					}
				}
			}

			// 获取用户数据
			try {
				// var user = new ktluser2().userInfo(false);
				// ktlsetting.opuid = user.baseInfo.ID;

				var user = new ktluser().userInfo(false);
				ktlsetting.opuid = user.id;
			} catch (e) {
				ktlsetting.opuid = 0;
			}
			//debugger;
			// 请求地址
			let openurl = "";
			// 请求参数
			let params = "";
			// 处理api请求地址
			if (opts.url || (!isEmpty(opts.apiurl) && !Object.entries(OT).map(item => item[1]).includes(opts.apiurl))) {

				// 请求参数组装
				if (!isEmptyObj(opts.data)) {
					var str = '';
					var pd = new PostData()
					for (var key in defaults.data) {
						str += key + '=' + pd.dataFormat(defaults.data[key]) + '&';
					}
					params += str.substring(0, str.length - 1);
				}

				// 请求地址组装：非DataMamager请求
				if (opts.url) {
					openurl = opts.url + "?list=" + ktlsetting.islist + "&OU=" + ktlsetting.opuid;;
				} else {
					openurl = apiurl + "?list=" + ktlsetting.islist + "&OU=" + ktlsetting.opuid;;
					if (isEmpty(params)) {
						params += "apiurl=" + opts.apiurl;
					} else {
						params = `apiurl=${opts.apiurl}&${params}`;
					}
				}

			} else {
				// 请求地址组装：managerApi
				openurl = apiurl + "?list=" + ktlsetting.islist + "&OU=" + ktlsetting.opuid;
				// 请求参数校验
				if (!ktlsetting.auto && isEmpty(opts.name)) {
					return MsgBox.error(`请传递name参数`)
				}

				// 请求参数组装
				composition: {
					// 传参为字符串
					if (isString(opts.data)) {
						params += opts.data;
						break composition;
					}

					// 传参为对象
					const pd = new PostData();
					if (!isEmptyObj(opts.data)) {
						// 批处理，data应该是数组
						if (opts.apiurl === _defaule_ot.ProcAddSet) {
							// 参数格式校验
							if (!isArray(opts.data)) {
								MsgBox.error(`${_defaule_ot.ProcAddSet}请求的参数应该是数组`)
								return
							}

							opts.data.forEach(item => {
								pd.addItem(item)
							})
						} else {
							for (let key in opts.data) {
								pd.add(key, opts.data[key])
							}
						}
					}

					// 默认使用Proc
					if (isEmpty(opts.apiurl)) {
						opts.apiurl = _defaule_ot.Proc
					}

					params += `ApiUrl=${per + opts.apiurl}&Name=${opts.name}&Parameters=${pd.toString()}`;
				}
			}


			defaults.method = defaults.method.toUpperCase();
			defaults.cache = defaults.cache ? '' : '&' + new Date().getTime();
			// 处理GET请求的 url
			if (defaults.method === 'GET' && (params || defaults.cache)) {
				if (openurl.indexOf("?") != -1) {
					openurl += '?' + params + defaults.cache;
				} else {
					openurl += '&' + params + defaults.cache;
				}
			}

			var oXhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');

			//oXhr.setRequestHeader("Content-Length", data.length);
			//oXhr.setRequestHeader("token", "AAAAA");
			oXhr.open(defaults.method, openurl, defaults.async);
			if (opts.dataType) {
				console.log("opts.dataType", opts.dataType)
				oXhr.setRequestHeader("Content-Type", opts.dataType);
			}
			if (defaults.method === 'GET') {
				oXhr.send(null);
			} else {
				try {
					// 打开加载等待层
					ktlsetting.mask && loaddingPlus(true);
					// 发送网络数据
					oXhr.send(params);
				} catch (e) {
					console.log(e);
				}
			}

			oXhr.onreadystatechange = function () {

				if (oXhr.readyState === 4) {
					// 释放请求锁定
					ktlapi.requestMap[paramsStr] = false;
					// 关闭加载等待层
					ktlsetting.mask && loaddingPlus(false);

					// 请求成功
					if (oXhr.status === 200) {
						//console.log(JSON.parse(oXhr.responseText))
						resolve(JSON.parse(oXhr.responseText))
					} else {
						//console.log(oXhr);

						MsgBox.error("网络异常")
						if (apierrorTime == 1) {
							//window.location.href = '/login.html';
							//alert('登录超时,请重新登录');
							//console.log("Server Error");
						}
						apierrorTime++;
						reject()
					}
				}
			};
		})

	}

	// 用api服务
	function res_ktlapi(opts, others = {}) {
		return ktlapi(opts, others, 'res')
	}

	function info_ktlapi(opts, others = {}) {
		return ktlapi(opts, others, 'info')
	}

	// MessageBox 消息提示框
	var MsgBox = function (msg, type = "info") {
		if (window.vueApp) {
			if (window.vueApp.$messageBox) {
				window.vueApp.$messageBox({
					type: type,
					message: msg
				})
			} else {
				window.vueApp.$message({
					type: type,
					message: msg
				})
			}
		} else {
			alert(msg)
		}
	}
	MsgBox.success = function (msg) {
		MsgBox(msg, "success")
	}
	MsgBox.error = function (msg) {
		MsgBox(msg, "error")
	}
	MsgBox.warning = function (msg) {
		MsgBox(msg, "warning")
	}
	MsgBox.confirm = function (obj, callback) {
		const defaults = {
			title: "提示",
			message: "",
			okbtn: "确定",
			cancelbtn: "取消",
			type: "warning",
		}

		if (isString(obj)) {
			defaults.message = obj;
		} else {
			for (const [key, value] of Object.entries(obj)) {
				defaults[key] = value;
			}
		}

		(window.vueApp.$messageBox || window.vueApp.$msgbox).confirm(
			defaults.message,
			defaults.title,
			{
				confirmButtonText: defaults.okbtn,
				cancelButtonText: defaults.cancelbtn,
				type: defaults.type,
			}
		).then(() => {
			callback && callback(true, false)
		}).catch(() => {
			callback && callback(false, true)
		})
	}

	// Notify 消息提示框
	function Notify(options) {
		if (isEmpty(options)) return

		let defaults = {
			title: "提示",
			message: "消息提示内容",
			type: "info"
		}

		// 如果是字符串，作为消息内容显示
		if (isString(options)) {
			defaults.message = options;
		}
		// 如果是对象，则将内容赋值给 defaults
		else if (isObject(options)) {
			for (let key in options) {
				defaults[key] = options[key]
			}
		}

		// 使用 ElementPlus 的 Notify
		if (_.get(window, "ElementPlus", false)) {
			return ElementPlus.ElNotification(defaults)
		}
		// 使用 ElementUI 的 Notify
		if (_.get(window, "ELEMENT", false)) {
			return ELEMENT.Notification(defaults)
		}
		// 默认提示方式
		alert(defaults.message);
	}

	function NotifyTypeMessage(type, message, duration) {
		let default_message = "消息提示内容";
		if (!isEmpty(message)) {
			default_message = message;
		}

		Notify({
			title: "提示",
			message: default_message,
			type,
			duration
		})
	}
	/**
	 * Notify 成功类型消息提示
	 * @param {any} message 消息内容
	 * @param {any} duration 显示时间, 毫秒。设为 0 则不会自动关闭
	 */
	Notify.success = function (message = "", duration = 4500) {
		NotifyTypeMessage("success", message, duration);
	}
	/**
	 * Notify 警告类型消息提示
	 * @param {any} message 消息内容
	 * @param {any} duration 显示时间, 毫秒。设为 0 则不会自动关闭
	 */
	Notify.warning = function (message = "", duration = 4500) {
		NotifyTypeMessage("warning", message, duration);
	}
	/**
	 * Notify 错误类型消息提示
	 * @param {any} message 消息内容
	 * @param {any} duration 显示时间, 毫秒。设为 0 则不会自动关闭
	 */
	Notify.error = function (message = "", duration = 4500) {
		NotifyTypeMessage("error", message, duration);
	}


	// 解构请求结果
	function deconResponse(resp) {
		if (resp?.data?.tables) {
			return resp.data.tables
		}
		return resp.data.table
	}

	function ktlupfile(file, action, finish, progress, error) {
		return baseUpfile("", file, action, finish, progress, error);
	}
	function baseUpfile(basePath, file, action, finish, progress, error) {
		console.log(file);

		let fileData = new FormData();
		let xhr = new XMLHttpRequest();
		if (typeof (file.length) == 'undefined') {
			fileData.append("files", file)
		} else {
			for (var i = 0; i < file.length; i++) {
				fileData.append("file" + i, file[i]);
			}
		}

		//console.log(fileData); 

		xhr.onreadystatechange = function () {
			if (xhr.readyState === 4) {
				if (xhr.status >= 200 && xhr.status < 300) {
					//上传成功
					var data = JSON.parse(xhr.responseText);
					finish(data);
				} else {
					if (error) {
						error();
					}
				}
			} else {
				console.log('等待上传...');
				//item["status"] = "队列中...";
			}
		}

		xhr.upload.onprogress = function (e) {
			//console.log(e);
			if (e.lengthComputable) {
				var percentComplete = ((e.loaded / e.total) * 100).toFixed(2) + '%';
				if (progress) {
					progress(percentComplete);
				}
			}
		}

		var par = '?';
		for (var key in action) {
			par += key + "=" + action[key] + "&";
		}
		xhr.open("POST", basePath + "/ApiFilter/UploadFiles.ashx" + par, true)
		xhr.send(fileData);
	}

	function api_ktlupfile(file, action, finish, progress, error) {
		if (location.origin == "https://api.abc5g.com" || GetQueryString("dev") == "1") {
			return baseUpfile("", file, action, finish, progress, error)
		}
		return baseUpfile("/base", file, action, finish, progress, error)
	}

	ktlapi.requestMap = [];


	// KtlApi 参数组装工具
	class PostData {
		constructor() {
			this.data = [];
		}

		dataFormat(value) {

			if (isUnde(value)) {
				return "";
			}

			try {
				return value.toString()
					.replace(/\+/g, "%2b")
					.replace(/=/g, "%3d")
					.replace(/&/g, "%26")
					.replace(/'/g, '$*$*$')
					.replace(/\\/g, '$!$!$')
					.replace(/"/g, '$@$@$')
					.replace(/\n\r/g, "\\n\\r")
					.replace(/\r\n/g, "\\r\\n")
					.replace(/\r/g, "\\r")
					.replace(/\n/g, "\\n");
			} catch (e) {
				console.log("PostData.dataFormat ==> 数据转换失败：");
				console.log(`value=${value.toString()}`);
				console.log("");
				return "";
			}
		}

		add(key, value) {

			let resultData = {};
			try {
				let tmp = '{ "' + key + '": "' + this.dataFormat(value) + '" }';
				resultData = JSON.parse(tmp);
			} catch (e) {
				console.log("PostData.add ==> 字符串转换JSON失败：");
				console.log(`key=${key}, value=${value.toString()}`);
				console.log(e);
				console.log("");
			}

			this.data.push(resultData);
		}

		addItem(item) {
			for (let key in item) {
				//console.log(key + ':' + item[key]);
				item[key] = this.dataFormat(item[key]);
			}

			this.data.push(item);
		}

		toString() {
			try {
				let result = JSON.stringify(this.data);
				result = result.replace(/"/g, "'"); //.replace(/###/g, '"');
				return result;
			} catch (e) {
				console.log("PostData.toString ==> JSON转换字符串失败：");
				console.log(e);
				console.log("");
			}

			return ""
		}
	}

	// 列表转树
	function treeJson(json, id, pid, formatCall) {

		let data = JSON.parse(JSON.stringify(json));
		if (!data || !data.length) return [];
		let targetData = [];                    //存储数据的容器(返回)
		let records = {};
		let itemLength = data.length;           //数据集合的个数
		for (let i = 0; i < itemLength; i++) {
			let o = data[i];
			records[o[id]] = o;
		}
		for (let i = 0; i < itemLength; i++) {
			let currentData = data[i];
			formatCall && formatCall(currentData)
			let parentData = records[currentData[pid]];
			if (!parentData) {
				targetData.push(currentData);
				continue;
			}
			parentData.children = parentData.children || [];
			parentData.children.push(currentData);
		}
		return targetData;
	}

	// 将树结构转换为列结构的函数
	function flattenTree(tree) {
		let result = [];

		function flatten(node) {
			const { children = [], ...values } = node;
			result.push(values);
			children.forEach(child => flatten(child));
		}
		if (isArray(tree)) {
			tree.forEach(child => flatten(child));
		} else {
			flatten(tree);
		}

		return result;
	}

	function pageJson(json, currentPage, pagePercount) {

		for (var i = 0; i < json.length; i++) {
			json[i]["ShowInPage"] = Math.floor(i / pagePercount) + 1;
			json[i]["IsInPage"] = false;
			if (json[i]["ShowInPage"] == currentPage) {
				json[i]["IsInPage"] = true;
			}
		}
		return json;
	}

	function distinctJson(json, key, value) {
		var temp = [];
		temp.push({ "Key": 0, "Value": "未选择" })
		for (var i = 0; i < json.length; i++) {
			var exist_grade = false;
			for (var j = 0; j < temp.length; j++) {
				if (temp[j]["Key"] == json[i][key]) {
					exist_grade = true;
					break;
				}
			}
			if (!exist_grade) {
				temp.push({ "Key": json[i][key], "Value": json[i][value] });
			}
		}
		return temp;
	}

	function childJson(json, columnValue, columnLabel, deep) {
		var temp = [];
		var orgJson = [];
		if (deep) {
			orgJson = JSON.parse(JSON.stringify(json));
		} else {
			orgJson = json;
		}
		for (var i = 0; i < orgJson.length; i++) {
			var exist = false;
			for (var j = 0; j < temp.length; j++) {
				//console.log(temp[j]["title"] +":"+ json[i][columnName]);
				if (temp[j]["value"] == orgJson[i][columnValue]) {
					exist = true;
					//console.log(temp[j]["title"]);
					temp[j]["children"].push(orgJson[i]);
					break;
				} else {
					exist = false;
				}
			}
			if (!exist) {
				var data = {};
				if (typeof (commondColumn) != 'undefined') {
					data[commondColumn[k]] = orgJson[i][columnLabel];
				}
				temp.push({
					"value": orgJson[i][columnValue],
					"label": orgJson[i][columnLabel],
					"children": [orgJson[i]]
				})
			}
		}
		return temp;
	}
	function pivotJson2(json, columnName, commondColumn, deep) {
		var temp = [];
		var orgJson = [];
		if (deep) {
			orgJson = JSON.parse(JSON.stringify(json));
		} else {
			orgJson = json;
		}
		for (var i = 0; i < orgJson.length; i++) {
			var exist = false;
			for (var j = 0; j < temp.length; j++) {
				//console.log(temp[j]["title"] +":"+ json[i][columnName]);
				if (temp[j]["title"] == orgJson[i][columnName]) {
					exist = true;
					//console.log(temp[j]["title"]);
					temp[j]["items"].push(orgJson[i]);
					break;
				} else {
					exist = false;
				}
			}
			if (!exist) {
				var data = {};
				if (typeof (commondColumn) != 'undefined') {
					for (var k = 0; k < commondColumn.length; k++) {
						data[commondColumn[k]] = orgJson[i][commondColumn[k]];
					}
				}
				temp.push({
					"title": orgJson[i][columnName],
					"data": data,
					"items": [orgJson[i]]
				})
				//console.log(data);
			}
		}
		var result = [];
		for (var i = 0; i < temp.length; i++) {
			temp[i].data.items = temp[i].items;
			result.push(temp[i].data);
		}
		return result;
	}

	function pivotJson(json, columnName, commondColumn, deep) {
		var temp = [];
		var orgJson = [];
		if (deep) {
			orgJson = JSON.parse(JSON.stringify(json));
		} else {
			orgJson = json;
		}
		for (var i = 0; i < orgJson.length; i++) {
			var exist = false;
			for (var j = 0; j < temp.length; j++) {
				//console.log(temp[j]["title"] +":"+ json[i][columnName]);
				if (temp[j]["title"] == orgJson[i][columnName]) {
					exist = true;
					//console.log(temp[j]["title"]);
					temp[j]["items"].push(orgJson[i]);
					break;
				} else {
					exist = false;
				}
			}
			if (!exist) {
				var data = {};
				if (typeof (commondColumn) != 'undefined') {
					for (var k = 0; k < commondColumn.length; k++) {
						data[commondColumn[k]] = orgJson[i][commondColumn[k]];
					}
				}
				temp.push({
					"title": orgJson[i][columnName],
					"data": data,
					"items": [orgJson[i]]
				})
				//console.log(data);
			}
		}
		return temp;
	}

	function unpivotJson(json, deep) {
		var temp = [];
		var orgJson = [];
		if (deep) {
			orgJson = JSON.parse(JSON.stringify(json));
		} else {
			orgJson = json;
		}
		for (var i = 0; i < orgJson.length; i++) {
			for (var j = 0; j < orgJson[i].items.length; j++) {
				temp.push(orgJson[i].items[j]);
			}
		}
		return temp;
	}

	function ktluser() {

		this.period = function () {
			//alert('period');
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				this.logoff();
			}
			var info = JSON.parse(localStorage["userinfo"]);
			//if (info.period=='') {
			//	this.logoff();
			//      }
			if (new Date(info.period) < new Date()) {
				this.logoff();
			}
		}

		this.login = function (username, password, callback) {
			var pd = new PostData();
			pd.add("username", username);
			pd.add("password", password);
			var that = this;
			ktlapi({
				data: {
					"apiurl": OT.ProcSet,
					"Name": "UserLogin",
					"Parameters": pd.toString()
				},
				success: function (data) {
					var role = data.data.tables[0].table;
					var model = data.data.tables[1].table;
					var ret = {};
					if (role.length == 0) {
						ret = {
							"error": true,
							"msg": "用户名密码错误"
						}
					} else {
						for (var i = 0; i < role.length; i++) {
							if (i == 0) {
								role[i]["main"] = true;
							} else {
								role[i]["main"] = false;
							}
						}

						var teacher = [];
						if (data.data.tables[2].table.length > 0) {
							teacher = data.data.tables[2].table;
						}
						var student = {};
						if (data.data.tables[3].table.length > 0) {
							student = data.data.tables[3].table[0];
						}

						var cfgrole = data.data.tables[4].table;
						//for (var i = 0; i < cfgrole.length; i++) {
						//	if (cfgrole["Role"] == 3)
						//}
						//var schools = [];
						//if (data.data.tables[2].table.length > 0) {
						//	schools = data.data.tables[2].table;
						//}

						//var classes = [];
						//if (data.data.tables[3].table.length > 0){
						//	classes = data.data.tables[3].table;
						//}

						//var patrol = [];
						//if (data.data.tables[6].table.length > 0) {
						//	patrol = data.data.tables[6].table;
						//}
						var rolemapping = pivotJson(data.data.tables[5].table, "Name", ["RoleId", "Name"]);
						ret = {
							"error": false,
							"msg": "登录成功",
							"id": role[0]["AccountId"],
							"name": role[0]["Name"],
							"period": role[0]["Preiod"],
							"pwd": role[0]["pwd"],
							"roles": role,
							"models": model,
							"teacher": teacher,
							"student": student,
							"manage": cfgrole,
							"mapping": rolemapping
						}
						console.log(ret);
						localStorage["userinfo"] = JSON.stringify(ret);
					}
					callback(ret);
				}
			}, { "log": true });
		}


		this.getMyJoin = function () {
			//alert('getMyJoin');
			this.period();
			var list = { "Schools": [], "Classes": [] };
			var info = this.userInfo();

			if (typeof (info.manage) == 'undefined') {
				this.logoff();
				return;
			}
			if (typeof (info.mapping) == 'undefined') {
				this.logoff();
				return;
			}

			for (var i = 0; i < info.teacher.length; i++) {
				var isschooljoined = false;
				for (var j = 0; j < list["Schools"].length; j++) {
					if (list["Schools"][j] == info.teacher[i]["SchoolId"]) {
						isschooljoined = true;
						break;
					}
				}
				if (!isschooljoined) {
					list["Schools"].push(info.teacher[i]["SchoolId"]);
				}

				var isclassjoined = false;
				for (var j = 0; j < list["Classes"].length; j++) {
					if (list["Classes"][j] == info.teacher[i]["ClassId"]) {
						isclassjoined = true;
						break;
					}
				}
				if (!isclassjoined) {
					list["Classes"].push(info.teacher[i]["ClassId"]);
				}
			}
			for (var i = 0; i < info.manage.length; i++) {
				var isschooljoined = false;
				for (var j = 0; j < list["Schools"].length; j++) {
					if (list["Schools"][j] == info.manage[i]["SchoolId"]) {
						isschooljoined = true;
						break;
					}
				}
				if (!isschooljoined) {
					list["Schools"].push(info.manage[i]["SchoolId"]);
				}

				var isclassjoined = false;
				for (var j = 0; j < list["Classes"].length; j++) {
					if (list["Classes"][j] == info.manage[i]["ClassId"]) {
						isclassjoined = true;
						break;
					}
				}
				if (!isclassjoined) {
					list["Classes"].push(info.manage[i]["ClassId"]);
				}
			}
			if (true) {
				if (typeof (info.student["SchoolId"]) != 'undefined') {
					list["Schools"].push(info.student["SchoolId"]);
					list["Classes"].push(info.student["ClassId"]);
				}
			}



			if (this.hasRole(1)) {
				list.Schools = [0];
				list.Classes = [0];
			}
			if (list.Schools.length == 0) {
				list.Schools = [-1111111]
			}
			if (list.Classes.length == 0) {
				list.Classes = [-1111111]
			}
			if (typeof (list.Schools[list.Schools.length - 1]) == 'undefined') {
				list.Schools.splice(list.Schools.length - 1, 1);
			}
			if (typeof (list.Classes[list.Classes.length - 1]) == 'undefined') {
				list.Classes.splice(list.Classes.length - 1, 1);
			}
			return list;
		}

		this.validpwd = function () {
			//alert('validpwd');
			var uf = this.userInfo();
			if (typeof (uf["pwd"]) == 'undefined') {
				this.logoff();
				return;
			}
			var pd = new PostData();
			pd.add("id", uf["id"]);
			pd.add("pwd", uf["pwd"]);
			var that = this;
			ktlapi({
				data: {
					"apiurl": OT.Proc,
					"Name": "ValidPwd",
					"Parameters": pd.toString()
				},
				success: function (data) {
					if (data.data.table[0]["Cnt"] != 1) {
						that.logoff();
					}
				}
			}, { "mask": false });
		}

		this.logoff = function (callback) {
			//alert('logoff');
			localStorage.removeItem("userinfo");
			window.location = '/login.html';
		}

		this.userInfo = function (valid) {
			//alert('userinfo');
			if (typeof (valid) == 'undefined') {
				valid = true;
			}
			if (valid) {
				this.period();
			}
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				return null;
			}
			return JSON.parse(localStorage["userinfo"]);
		}

		//this.id = (this.userInfo() == null ? '' : this.userInfo().id);

		this.currentRole = function () {
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				return null;
			}
			var roles = this.getRoles();
			for (var i = 0; i < roles.length; i++) {
				if (roles[i]["main"]) {
					return roles[i];
				}
			}
			return null;
		}

		this.hasRole = function (roleid) {
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				return false;
			}
			var roles = this.getRoles();
			for (var i = 0; i < roles.length; i++) {
				if (roles[i]["RoleId"] == roleid) {
					return true;
				}
			}
			return false;
		}

		this.changeRole = function (roleid) {
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				return null;
			}
			var roles = this.getRoles();
			var temp = {};
			for (var i = 0; i < roles.length; i++) {
				if (roles[i]["RoleId"] == roleid) {
					temp = roles[i];
					roles[i]["main"] = true;
				} else {
					roles[i]["main"] = false;
				}
			}
			this.reset("roles", roles);
			return temp;
		}

		this.reset = function (key, data) {
			var local = JSON.parse(localStorage["userinfo"]);
			local[key] = data;
			localStorage["userinfo"] = JSON.stringify(local);
		}

		this.getRoles = function () {
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				return [];
			}
			this.validpwd();
			return JSON.parse(localStorage["userinfo"])["roles"];
		}

		this.valid = function () {
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				return false;
			}
			return true;
		}

		this.getModel = function (role) {
			if (typeof (localStorage["userinfo"]) == 'undefined') {
				return null;
			}
			return JSON.parse(localStorage["userinfo"])["models"];
		}
	}


	function ktluser2() {
		this.isLogin = function () {
			if (this.userInfo() == null) {
				return false;
			}
			return true;
		}

		this.reset = function (data) {
			KtlStorage.setItem("userinfo", data)
			//localStorage["userinfo"] = JSON.stringify(data);
		}

		this.login = function (username, password, callback) {
			info_ktlapi({
				apiurl: OT.ProcSet,
				name: "BaseLogin",
				data: {
					username: username,
					password: password,
				}
			}).then(resp => {
				var ret = {};
				if (resp.data.tables.length == 0) {
					ret.result = false;
					ret.id = 0;
					ret.msg = "用户名密码错误";
				} else {
					ret.result = true;
					ret.msg = "localStorage---userinfo";

					var userdata = {};
					userdata.baseInfo = resp.data.tables[0].table[0];
					ret.id = userdata.baseInfo.ID;
					userdata.baseRole = [];
					if (resp.data.tables[1].table.length > 0) {
						userdata.baseRole = resp.data.tables[1].table;
					}
					KtlStorage.setItem("userinfo", userdata)
					//localStorage["userinfo"] = JSON.stringify(userdata);
				}
				callback(ret);
			})
		}

		this.userInfo = function (valid) {
			let user = KtlStorage.getItem("userinfo")
			if (isEmptyObj(user)) {
				return null;
			}
			if (typeof (valid) != 'undefined' && valid) {
				this.valid();
			}

			return user;
		}

		this.hasRole = function (roleid) {
			var user = this.userInfo();
			if (user == null) {
				return false;
			}

			if (isUnde(user.baseRole)) {
				return false
			}

			var temp = user.baseRole.filter(x => { return x.ID == roleid });
			if (temp.length > 0) {
				return true;
			} else {
				return false;
			}
		}

		this.valid = function () {
			//alert('validpwd');
			var uf = this.userInfo();
			if (uf == null) {
				return;
			}
			if (typeof (uf.baseInfo["Token"]) == 'undefined') {
				this.logoff();
				return;
			}

			res_ktlapi({
				apiurl: OT.ProcSet,
				name: "ValidToken",
				data: {
					ID: uf.baseInfo["ID"],
					Token: uf.baseInfo["Token"],
				}
			}).then(resp => {
				if (resp.data.table[0]["Cnt"] != 1) {
					this.logoff();
				}
			}, { "mask": false });

		}

		this.logoff = function () {
			//alert('logoff');
			KtlStorage.removeItem("userinfo");
			//localStorage.removeItem("userinfo");
			window.location = '/login.html';
		}
	}


	// 获取GUID
	function guid() {

		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

			var r = Math.random() * 16 | 0,

				v = c == 'x' ? r : (r & 0x3 | 0x8);

			return v.toString(16);

		});

	}

	// 获取所有叶子节点ID
	function getAllLeafNodeId(tree, mapId = "id", mapChildren = "children") {
		if (isEmptyObj(tree)) return []

		if (isArray(tree)) {
			let result = []
			tree.forEach(item => {
				if (isEmptyObj(item[mapChildren])) {
					result.push(item[mapId])
				} else {
					result = [...result, ...getAllLeafNodeId(item[mapChildren], mapId, mapChildren)]
				}
			})

			return result
		} else {
			return getAllLeafNodeId([tree], mapId, mapChildren)
		}
	}


	// 根据ID获取树节点
	function getTreeNodeById(tree, id, mapId = "id", mapChildren = "children") {
		if (isEmptyObj(tree)) return []

		if (isArray(tree)) {
			let result = {}
			tree.some(item => {
				if (item[mapId] == id) {
					result = item
					return true;
				}

				const deep = getTreeNodeById(item[mapChildren], id, mapId)
				if (!isEmptyObj(deep)) {
					result = deep;
					return true
				}
			})

			return result
		} else {
			return getTreeNodeById([tree], id, mapId)
		}
	}

	// 获取机型
	function getAgent() {
		var u = navigator.userAgent;
		var app = navigator.appVersion;
		var agn = {//移动终端浏览器版本信息
			trident: u.indexOf("Trident") > -1, //IE内核
			presto: u.indexOf("Presto") > -1, //opera内核
			webKit: u.indexOf("AppleWebKit") > -1, //苹果、谷歌内核
			gecko: u.indexOf("Gecko") > -1 && u.indexOf("KHTML") == -1, //火狐内核
			mobile: !!u.match(/AppleWebKit.*Mobile.*/) || !!u.match(/AppleWebKit/), //是否为移动终端
			ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
			android: u.indexOf("Android") > -1 || u.indexOf("Linux") > -1, //android终端或者uc浏览器
			iPhone: u.indexOf("iPhone") > -1 || u.indexOf("Mac") > -1, //是否为iPhone或者QQHD浏览器
			iPad: u.indexOf("iPad") > -1, //是否iPad
			webApp: u.indexOf("Safari") == -1 //是否web应该程序，没有头部与底部
		};
		console.log(agn);
		return agn;
	}

	function getBrower() {
		var u = navigator.userAgent;
		var br = {//移动终端浏览器版本信息
			IE: u.indexOf("Edg/") > -1, //IE内核 
			Google: u.indexOf("Chrome/") > -1,
			Firefox: u.indexOf("Firefox/") > -1
		};
		console.log(br);
		return br;
	}

	function jsonToExcel(data, head, name = '导出的文件名') {
		let str = head ? head + '\n' : '';
		data.forEach(item => {
			// 拼接json数据, 增加 \t 为了不让表格显示科学计数法或者其他格式
			for (let key in item) {
				str = `${str + item[key] + '\t'},`
			}
			str += '\n'
		});
		console.log(str)
		// encodeURIComponent解决中文乱码
		const uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str);
		// 通过创建a标签实现
		const link = document.createElement("a");
		link.href = uri;
		// 对下载的文件命名
		link.download = `${name + '.csv'}`;
		link.click();
	}

	function dateFormat(val = null, format = "YYYY-MM-DD") {

		if (isEmptys([val, format])) return val;
		if (!isString(format)) return val;

		try {
			let date = new Date(val);
			const fullYear = date.getFullYear(),
				month = date.getMonth() + 1,
				dateday = date.getDate(),
				hours = date.getHours(),
				minutes = date.getMinutes(),
				seconds = date.getSeconds()

			const fm = {
				YYYY: fullYear,
				yyyy: fullYear,
				YYY: fullYear,
				yyy: fullYear,
				YY: fullYear.toString().padStart(2, '0'),
				yy: fullYear.toString().padStart(2, '0'),
				Y: fullYear % 100,
				y: fullYear % 100,
				MM: month.toString().padStart(2, '0'),
				M: month,
				DD: dateday.toString().padStart(2, '0'),
				dd: dateday.toString().padStart(2, '0'),
				D: dateday,
				d: dateday,
				HH: hours.toString().padStart(2, '0'),
				hh: hours.toString().padStart(2, '0'),
				H: hours,
				h: hours,
				mm: minutes.toString().padStart(2, '0'),
				m: minutes,
				ss: seconds.toString().padStart(2, '0'),
				s: seconds
			}

			let res = format;
			for (const [key, value] of Object.entries(fm)) {
				res = res.replace(new RegExp(key, 'g'), value);
			}

			return res;
		} catch (e) {
			console.log(e)
			return val;
		}
	}


	class KtlStorage {

		domainKey = ""
		context = null

		constructor(domainKey) {
			this.domainKey = domainKey;
			if (!isEmpty(domainKey)) {
				if (isEmpty(KtlStorage.getItem(domainKey))) {
					KtlStorage.setItem(domainKey, {})
				}
			}
		}

		static setItem(key, value) {
			if (isEmpty(key) || !isString(key)) return

			// 类型处理
			let dataType = typeof (value);
			if (value instanceof Date) {
				dataType = 'date';
			}

			// 固定格式存储
			const obj = JSON.stringify({
				dataType,
				content: value
			})

			localStorage.setItem(key, obj)
		}

		static getItem(key) {
			if (isEmpty(key) || !isString(key)) return

			let _storage = localStorage.getItem(key);

			if (!isEmpty(_storage)) {
				_storage = JSON.parse(_storage);

				switch (_storage.dataType) {
					case 'number': _storage = _storage.content / 1; break;
					case 'string':
					case 'boolean':
					case 'object': _storage = _storage.content; break;
					case 'date': _storage = new Date(_storage.content); break;
					default: _storage = _storage.content;
				}
			}

			return _storage
		}

		static removeItem(key) {
			if (isEmpty(key) || !isString(key)) return

			localStorage.removeItem(key)
		}

		static createDomainCache(domainKey) {
			if (isEmpty(domainKey, true)) {
				console.warn("KtlStorage：缺少pageKey参数，工具类将无法使用")
			}
			return new KtlStorage(domainKey);
		}

		bindContext(context) {
			this.context = context;
			return this
		}

		setItem(key, value) {
			if (isEmpty(this.domainKey)) return
			if (isEmpty(key)) {
				console.warn("DomainCache.setCache：缺少key参数，操作无效")
				return
			}

			// 如果有上下文，同时更新上下文数据
			if (this.context) {
				_.set(this.context, key, value)
			}

			// 解析所有keys
			const keyPath = key.split(".");
			// 第一个key
			const firstKey = keyPath[0];

			let data = KtlStorage.getItem(this.domainKey)
			if (keyPath.length > 1) {
				_.set(data, key.substring(firstKey.length + 1), value)
			} else {
				_.set(data, firstKey, value)
			}

			KtlStorage.setItem(this.domainKey, data)

			return this
		}

		getItem(key) {
			if (isEmpty(this.domainKey)) return
			if (isEmpty(key)) {
				console.warn("DomainCache.getCache：缺少key参数，操作无效")
				return
			}

			let data = KtlStorage.getItem(this.domainKey)
			return data[key]
		}

		hasItem(key, fn) {
			console.log('hasItem');
			
			if (isEmpty(this.domainKey)) return
			if (isEmpty(key)) {
				console.warn("DomainCache.hasItem：缺少key参数，操作无效")
				return
			}

			//console.log("hasItem key", key)
			//console.log(`KtlStorage.getItem(this.domainKey)`, KtlStorage.getItem(this.domainKey))

			let data = KtlStorage.getItem(this.domainKey)

			console.log("isEmptyObj(data)", isEmptyObj(data))
			if (isEmptyObj(data)) return;
			//console.log("data[key]", data[key])
			if (!isEmpty(data[key])) {
				fn && fn();
				return true;
			}

			return false
		}

		getCache() {
			if (isEmpty(this.domainKey)) return
			return KtlStorage.getItem(this.domainKey) || {}
		}

		copyToContext(context) {

			let _defaultContext = this.context;
			if (!isEmpty(context)) {
				_defaultContext = context;
			}

			if (isEmpty(_defaultContext)) return

			//console.log("copyToContext", context)
			let data = this.getCache()
			for (const [key, value] of Object.entries(data)) {
				//console.log(key, value)
				_defaultContext[key] = value
			}

			return this
		}
	}

	/**
	 * 回到顶部
	 */
	function backTop(el) {
		try {
			if(el){
				$(el)[0].scrollTo({
					top: 0,
					left: 0,
					behavior: 'smooth' // 可选，平滑滚动效果
				});
			}else{
				$("#main-body").parent()[0].scrollTo({
					top: 0,
					left: 0,
					behavior: 'smooth' // 可选，平滑滚动效果
				});
			}
		} catch {
			window.scrollTo({
				top: 0,
				left: 0,
				behavior: 'smooth' // 可选，平滑滚动效果
			});
		}
	}

	function openElDialog(options) {
		let defaults = {
			title: "标题",
			width: "50%",
			component: undefined,
			props: {},
			data: {},
			callback: () => { }
		}

		if (!isEmptyObj(options)) {
			for (let key in options) {
				defaults[key] = options[key]
			}
		}

		let DialogId = 'dialog_' + guid().replace(/-/g, '');
		let DialogController = document.createElement('div');
		DialogController.id = DialogId;
		DialogController.innerHTML = `
			<el-dialog draggable title="${defaults.title}" v-model="visible" width="${defaults.width}">
				<body-component></body-component>
			</el-dialog>
		`;
		document.body.appendChild(DialogController);

		setTimeout(() => {
			const { h } = Vue;

			let dialogApp = null;

			const BodyComponent = {
				render() {
					return h(defaults.component, {
						...defaults.props,
						onClose: (data) => {
							console.log("关闭事件", data)
							if (dialogApp) {
								dialogApp.visible = false;
							}
						}
					})
				}
			}

			dialogApp = createApp({
				el: `#${DialogId}`,
				components: {
					BodyComponent
				},
				data() {
					return {
						visible: true
					}
				},
			})
		}, 200)
	}

	function openElDialogUrl(options = {}) {
		// 默认值
		let defaults = {
			url: "",
			title: "标题",
			width: "50%",
			top: "15vh",
			data: {},
			callback: () => { }
		}

		// 赋初值
		if (isObject(options)) {
			for (let key in options) {
				defaults[key] = options[key]
			}
		}

		// 启动等待层
		loaddingPlus(true)

		let DialogId = 'dialog_' + guid().replace(/-/g, '');
		let DialogContentId = 'content_' + guid().replace(/-/g, '');
		let DialogController = document.createElement('div');

		// 弹窗拖拽功能
		let draggable = 'draggable';
		if(Vue.version.startsWith('2')){
			draggable = 'v-dialogdrag'
		}

		DialogController.id = DialogId;
		DialogController.innerHTML = `
		<div id="${DialogId}">
			<el-dialog
				ref="eldialog" ${draggable}
				v-if="render"
				title="${defaults.title}"
				top="${defaults.top}"
				:visible.sync="visible"
				v-model="visible"
				width="${defaults.width}"
				:before-close="handleClose"
				:close-on-click-modal="false"
			>
				<div ref="dialogcontent" id="${DialogContentId}"></div>
			</el-dialog>
		<div>
    `;
		document.body.appendChild(DialogController);

		const _url = defaults.url;
		$.ajax({
			url: _url + (_url.indexOf("?") > 0 ? '&' : '?') + `routerid=${DialogId}`,
			success: function (code) {
				setTimeout(() => {
					let dialogApp = createApp({
						el: `#${DialogId}`,
						data() {
							return {
								visible: true,
								render: true,
							}
						},
						mounted() {
							// 消息订阅：弹窗内部页面调用关闭方法dialogClose的时候，会触发此回调函数
							window.$bus.subscribe(DialogId, data => {
								this.handleClose(data)
							}, "close")

							// 消息订阅：参数传递
							window.$bus.subscribe(DialogId, callback => {
								callback && callback(defaults.data)
							}, "data")


							// 加载内容组件
							this.$nextTick(() => {
								$("#" + DialogContentId).html(code.replace("createApp({", `createApp({diaRouterid:"${DialogId}",`))
							})
						},
						methods: {
							handleClose(data) {
								// 调用回调函数
								if (typeof data === "function") {
									defaults.callback && defaults.callback();
								} else {
									defaults.callback && defaults.callback(data);
								}
								// 销毁该消息订阅
								window.$bus.remove(DialogId)
								// 销毁组件
								dialogApp.render = false
								// 销毁元素
								document.getElementById(DialogId).remove();
								// 释放内存
								dialogApp = null;
							}
						}
					})
				}, 200)
			},
			error: function (err) {
				console.error(err);
				// 页面获取失败，关闭等待层
				loaddingPlus(false)
			}
		})
	}

	function openDialog(options = {}) {
		const { type = "url", ..._options } = options
		if (type === "url") {
			openElDialogUrl(_options)
		} else {
			openElDialog(_options)
		}
	}

	/**
	 * 页面转PDF
	 * @param title 保存后的文件名
	 * @param id ID选择器
	 */
	function pageToPdf(title, id) {
		if (isEmpty([html2canvas, jsPDF])) {
			ElementPlus.ElNotification({
				title: '提示',
				message: '需要引用api.abc5g.com/js/html2canvas.js',
				type: 'error',
				duration: 0
			})
			ElementPlus.ElNotification({
				title: '提示',
				message: '需要引用api.abc5g.com/js/jspdf.min.js',
				type: 'error',
				duration: 0
			})
			return
		}


		html2canvas(
			document.querySelector(id),
			{
				allowTaint: false,
				taintTest: false,
				logging: false,
				useCORS: true,
				dpi: window.devicePixelRatio * 4, //将分辨率提高到特定的DPI 提高四倍
				scale: 4, //按比例增加分辨率
			}
		).then((canvas) => {
			var pdf = new jsPDF('p', 'mm', 'a4'); //A4纸，纵向
			var ctx = canvas.getContext('2d'),
				a4w = 190,
				a4h = 272, //A4大小，210mm x 297mm，四边各保留10mm的边距，显示区域190x277
				imgHeight = Math.floor((a4h * canvas.width) / a4w), //按A4显示比例换算一页图像的像素高度
				renderedHeight = 0;

			while (renderedHeight < canvas.height) {
				var page = document.createElement('canvas');
				page.width = canvas.width;
				page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能内容不足一页

				//用getImageData剪裁指定区域，并画到前面创建的canvas对象中
				page
					.getContext('2d')
					.putImageData(
						ctx.getImageData(
							0,
							renderedHeight,
							canvas.width,
							Math.min(imgHeight, canvas.height - renderedHeight),
						),
						0,
						0,
					);
				pdf.addImage(
					page.toDataURL('image/jpeg', 1.0),
					'JPEG',
					10,
					10,
					a4w,
					Math.min(a4h, (a4w * page.height) / page.width),
				); //添加图像到页面，保留10mm边距

				renderedHeight += imgHeight;
				if (renderedHeight < canvas.height) {
					pdf.addPage(); //如果后面还有内容，添加一个空页
				}
				// delete page;
			}
			pdf.save(title + '.pdf')
		});
	}

	// 是否是事件对象
	const isEventObject = (obj)=> {
		return typeof obj === 'object' && obj !== null && 'target' in obj && 'currentTarget' in obj && 'eventPhase' in obj;
	}

	// 添加element-ui框架的Dialog的拖拽指令
	if(Vue.version.startsWith('2')){
		
		Vue.directive('dialogdrag', {
			bind: (el) => {
				// 获取弹框标题区域DOM节点
				const headerDOM = el.querySelector('.el-dialog__header');
				// 修改鼠标图标样式
				headerDOM.style.cursor = "move";
				// 禁止拖拽时选中标题中文本内容
				headerDOM.style.userSelect = "none";
				// 获取弹框区域的DOM节点
				const dialogDOM = el.querySelector('.el-dialog');
				let isDown = false,         // 是否按下
						// 鼠标按下时坐标位置
						clientX = 0,
						clientY = 0,
						// 按下时弹框位置
						dialogLeft = 0,
						dialogTop = 0;
				// 定义函数判断当前是否在可见范围内
				// function boundingRange(){
				// 	const bounding = dialogDOM.getBoundingClientRect();
				// 	return {
				// 		top: bounding.top >= 0,       // 表示顶部在可见范围
				// 		left: bounding.left >= 0,     // 表示左侧在可见范围
				// 		right: bounding.left < window.innerWidth - bounding.width,   // 表示右侧在指定范围
				// 		bottom: bounding.top < window.innerHeight - bounding.height   // 表示底部在指定范围
				// 	}
				// }
			  // 更新数据
				function update(e){
					// 获取当前鼠标按钮位置坐标
					clientX = e.clientX;
					clientY = e.clientY;
					// 获取弹框位置（默认情况弹框样式left和top可能不存在，当为NaN时初始化为0）
					dialogLeft = isNaN(parseFloat(dialogDOM.style.left))?0:parseFloat(dialogDOM.style.left);
					dialogTop = isNaN(parseFloat(dialogDOM.style.top))?0:parseFloat(dialogDOM.style.top);
				}
				// 监听鼠标按下事件
				headerDOM.onmousedown = e => {
					isDown = true;
					update(e);
				}
				// 监听鼠标移动事件
				window.onmousemove = e => {
					// 不按下的时候，执行移动操作
					if(isDown){
						// 获取DOM边界范围
						// const range = boundingRange();
						// 获取当前移动到的位置坐标,与按下位置坐标进行计算，获取移动距离
						const distX = e.clientX - clientX;          // distX小于0为向左，大于0为向右
						const distY = e.clientY - clientY;          // distY小于0为向上，大于0为向下
						// 判断左侧或右侧是否可移动
						// if((range.left && distX < 0) || (range.right && distX >= 0)) dialogDOM.style.left = (dialogLeft + distX) + "px";
						// 判断顶部或者底部是否可移动
						// if((range.top && distY < 0) || (range.bottom && distY >= 0)) dialogDOM.style.top = (dialogTop + distY) + "px";
						dialogDOM.style.left = (dialogLeft + distX) + "px";
						dialogDOM.style.top = (dialogTop + distY) + "px";
						// 更新起始位数据
						update(e);
					}
				}
				headerDOM.onmouseup = e => isDown = false;
				window.onmouseup = () => isDown = false;
			}
		})
	}

	/**
	 * 创建vue实例
	 * @param {Object} options 配置项
	 * @returns Vue实例
	 */
	function createApp(options = {}) {

		if (!Vue.version) return;

		const { el, mixins, diaRouterid, ajaxId, ...vueOptions } = options;

		if (isEmpty(el)) {
			alert("createApp: el是必传项")
			return;
		}

		// vue实例
		let appcase = null;
		let appOptions = {
			mixins: [{
				data() {
					return {
						ListLoadStatus: { ...ListLoadStatus },
						...(isEmpty(diaRouterid) ? {} : { diaRouterid, pageQuery: {} }),
						...(isEmpty(ajaxId) ? {} : { ajaxId, pageQuery: {} })
					}
				},
				mounted() {
					if (!window.vueApp) {
						window.vueApp = this;
					}

					if (!window.$bus) {
						window.$bus = BusMessage.init();
					}

					appcase = this;

					console.log("this.diaRouterid", this.diaRouterid)
					// 弹窗实例，初始化后关闭等待层
					if (!isEmpty(this.diaRouterid)) {
						// 初始化页面参数
						window.$bus.publish(this.diaRouterid, (data) => {
							this.pageQuery = JSON.parse(JSON.stringify(data || {}));
						}, "data")
						loaddingPlus(false)
					}

					// 异步页面实例
					if (!isEmpty(ajaxId)) {
						// 初始化页面参数
						window.$bus.publish(this.ajaxId, (data) => {
							this.pageQuery = JSON.parse(JSON.stringify(data || {}));
						}, "data")

						// 更新传参
						window.$bus.subscribe(this.ajaxId, (data) => {
							console.log("更新传参", this.ajaxId, data)
							this.pageQuery = JSON.parse(JSON.stringify(data || {}));
						}, "update-data")
					}

				},
				methods: {
					dateFormat(date, format){
						return dateFormat(date, format);
					},
					// 配合弹窗时使用
					dialogClose(data) {
						//console.log(data)
						if (!isEmpty(this.diaRouterid)) {
							if(isEventObject(data)){
								window.$bus.publish(this.diaRouterid, null, "close")
							} else {
								window.$bus.publish(this.diaRouterid, data, "close")
							}
						}
					},
					sendEmit(payload) {
						console.log('payload', payload, ajaxId);
						
						// 异步页面参数传递
						if (!isEmpty(ajaxId)) {
							// 初始化页面参数
							window.$bus.publish(this.ajaxId, isEventObject(payload) ? null : payload, "emit")
						}

						// 弹窗页面参数传递
						if (!isEmpty(this.diaRouterid)) {
							window.$bus.publish(this.diaRouterid, isEventObject(payload) ? null : payload, "data")
						}
					},
					// ElementPlus/ElementUI多字段校验
					formValidateFields(formRef, fields) {
						if (isEmpty(formRef)) return;
						if (isEmpty(fields)) return;

						let ref = formRef;
						if (isString(formRef)) {
							ref = this.$refs[formRef]
						}

						return new Promise((resolve, reject) => {
							let count = 0;
							ref.validateField(fields, (valid, err) => {
								// ElementPlus 的校验方式
								if (_.get(window, "ElementPlus", false)) {
									if (!valid) {
										for (let key in err) {
											err[key].forEach(item => {
												Notify.warning(item.message)
											})
										}
									} else resolve()
									return
								}

								// Element-UI 的校验方式
								if (_.get(window, "ELEMENT", false)) {
									if (!valid) {
										count++;
										if (count === fields.length) {
											resolve()
										}
									} else {
										Notify.warning(valid)
									}
								}
							})
						})
					}
				}
			}, ...(isArray(mixins) ? mixins : [])],
			...vueOptions
		}

		// Vue3 创建实例
		if (Vue.version.startsWith('3')) {
			const app = Vue.createApp(appOptions)
			// 注册ElementUI
			if (ElementPlus) {
				
				app.use(ElementPlus, {
					locale: ElementPlusLocaleZhCn
				});

				for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
					app.component(key, component)
				}
			}

			// 挂载
			app.mount(el)
			return appcase;
		}

		// Vue2 创建实例
		if (Vue.version.startsWith("2")) {
			return new Vue({
				el,
				...appOptions
			})
		}
	}

	// 注册组件
	var allCustomComponent = {};
	function setComponent(data) {

		console.log("setComponent", data)
		if (typeof (allCustomComponent[data.name]) == 'undefined') {
			allCustomComponent[data.name] = data
		} else {
			console.error("发现重名的自定义组件");
		}

		// 注册 Vue3 组件
		if (Vue.version.startsWith('3')) {
			
		}
		// 注册 Vue2 全局组件
		else if (Vue.version.startsWith('2')) {
			Vue.component(data.name, data)
		}
	}

	function getComponent(key) {
		if (isEmpty(key)) {
			return allCustomComponent;
		}

		return allCustomComponent[key]
	}

	// 消息通信工具
	class BusMessage {

		/** 生成一个 Bus 消息通讯 */
		static init() {
			return new BusMessage();
		}

		// 消息队列
		list = []

		/** 消息订阅 */
		subscribe(key, callback, routeid) {
			if (isEmptys([key, callback])) return;
			this.list.push({ key, callback, routeid })
		}

		/** 发布消息 */
		publish(key, data, routeid) {
			if (isEmpty(key)) return;
			this.list.filter(item => item.key === key).forEach(item => {
				if (isEmpty(routeid) || item.routeid === routeid) {
					item.callback(data);
				}
			})
		}

		/** 移除订阅消息 */
		remove(key, routeid) {
			const _list = this.list.filter(item => {
				if (item.key === key) {
					if (isEmpty(routeid)) {
						return false;
					}

					return item.routeid !== routeid;
				} else {
					return true;
				}
			})

			this.list = _list
		}
	}


	// 常量定义
	useAttrObjects({
		yun_hostname, yunConfig, ListLoadStatus, OT
	})

	// 定义全局变量
	useAttrObjects({
		c_url,
		apiurl,
		apierrorTime,
	})

	// 定义常用函数
	useAttrObjects({
		// 获取url参数
		GetQueryString,
		// 接口请求等待画面（遮罩层）
		loaddingPlus,
		// 解构请求结果
		deconResponse,
		// 文件上传通用函数
		baseUpfile,
		// 文件上传-当前站点
		ktl_upFile: ktlupfile,
		// 文件上传-api站点
		api_ktlupfile,
		// 消息提示框
		MsgBox,
		// Notify 消息提示框
		Notify,
		// 列表转树
		treeJson,
		flattenTree,
		pageJson,
		distinctJson,
		childJson,
		// 数据分组
		pivotJson,
		pivotJson2,
		unpivotJson,
		ktlapi2: ktlapi,
		res_ktlapi,
		info_ktlapi,
		ktluser,
		ktluser2,
		guid,
		getAllLeafNodeId,
		getTreeNodeById,
		getAgent,
		getBrower,
		jsonToExcel,
		dateFormat,
		KtlStorage,
		backTop,
		openElDialog,
		openElDialogUrl,
		openDialog2: openDialog,
		pageToPdf,
		createApp,
		setComponent,
		getComponent,
		BusMessage
	})

	// 初始化页面路由
	if (isUnde(window.usetheme)) {
		for (var i = 0; i < window.yunConfig.length; i++) {
			if (window.yunConfig[i].host == window.yun_hostname) {
				window.usetheme = window.yunConfig[i];
				break;
			}
		}
	}


	// https 重定向
	if (window.c_url.indexOf('http://') != -1) {
		// 本地不用替换
		const islocal = GetQueryString("islocal")
		if (islocal != '1') {
			window.location = c_url.replace("http://", "https://");
		}
	}

})()

